package com.xxx.rpc.client;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.UUID;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.xxx.rpc.client.filters.Filter;
import com.xxx.rpc.common.bean.RpcRequest;
import com.xxx.rpc.common.bean.RpcResponse;
import com.xxx.rpc.common.util.CollectionUtil;
import com.xxx.rpc.common.util.StringUtil;
import com.xxx.rpc.registry.ServiceDiscovery;

/**
 * RPC 代理（用于创建 RPC 服务代理）
 *
 * @author huangyong
 * @since 1.0.0
 */
public class RpcProxy {

    private static final Logger LOGGER = LoggerFactory.getLogger(RpcProxy.class);

    private String serviceAddress;
    private boolean register=true;
    private ServiceDiscovery serviceDiscovery;
    
    private List<Filter> filters=new ArrayList<Filter>();
    
    public RpcProxy() {
		super();
	}

    public RpcProxy(ServiceDiscovery serviceDiscovery,boolean register) {
		super();
		this.serviceDiscovery = serviceDiscovery;
		this.register = register;
	}



	public RpcProxy(ServiceDiscovery serviceDiscovery,List<Filter> filters,boolean register) {
        this.serviceDiscovery = serviceDiscovery;
        this.filters=filters;
        this.register = register;
    }

    public <T> T create(final Class<?> interfaceClass) {
        return create(interfaceClass, "");
    }

    @SuppressWarnings("unchecked")
    public <T> T create(final Class<?> interfaceClass, final String serviceVersion) {
        // 创建动态代理对象
        return (T) Proxy.newProxyInstance(
                interfaceClass.getClassLoader(),
                new Class<?>[]{interfaceClass},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // 创建 RPC 请求对象并设置请求属性
                        RpcRequest request = new RpcRequest();
                        request.setRequestId(UUID.randomUUID().toString());
                        request.setInterfaceName(method.getDeclaringClass().getName());
                        request.setServiceVersion(serviceVersion);
                        request.setMethodName(method.getName());
                        request.setParameterTypes(method.getParameterTypes());
                        request.setParameters(args);
                        String serviceName = interfaceClass.getName();
                        if (StringUtil.isNotEmpty(serviceVersion)) {
                            serviceName += "-" + serviceVersion;
                        }
                        if(register) {
                        	 // 获取 RPC 服务地址
                            if (serviceDiscovery != null) {
                                serviceAddress = serviceDiscovery.discover(serviceName);
                                LOGGER.info("discover service: {} => {}", serviceName, serviceAddress);
                            }
                        }else {
                        	Properties resolveDubbo=resolveDubboLocal();
                        	if (resolveDubbo != null) {
                                serviceAddress = resolveDubbo.getProperty(serviceName);
                                if(StringUtil.isEmpty(serviceAddress)) {
                                	LOGGER.info("discover service in local : {} => {}", serviceName, serviceAddress);
                                }
                            }
                        }
                        if (StringUtil.isEmpty(serviceAddress)) {
                            throw new RuntimeException("server address is empty");
                        }
                        // 从 RPC 服务地址中解析主机名与端口号
                        String[] array = StringUtil.split(serviceAddress, ":");
                        String host = array[0];
                        int port = Integer.parseInt(array[1]);
                        // 创建 RPC 客户端对象并发送 RPC 请求
                        RpcClient client = new RpcClient(host, port);
                        long time = System.currentTimeMillis();
                        RpcResponse response = null;
                        if(!CollectionUtil.isEmpty(filters)) {
                        	  response = this.doFilter(client, request);
                        }else {
                        	response = client.send(request);
                        }
                        //RpcResponse response = client.send(request);
                        LOGGER.info("time: {}ms", System.currentTimeMillis() - time);
                        if (response == null) {
                            throw new RuntimeException("response is null");
                        }
                        // 返回 RPC 响应结果
                        if (response.hasException()) {
                            throw response.getException();
                        } else {
                            return response.getResult();
                        }
                    }
                    
                    public RpcResponse doFilter(RpcClient invoker, RpcRequest invocation) throws Exception {
                		RpcResponse rpcResponse = null; 
                		for (Filter filter : filters) {
                			rpcResponse = filter.invoke(invoker, invocation);
                		}
                        return rpcResponse;
                	}
                }
        );
    }
    
    private Properties resolveDubboLocal() {
    	String userDir = System.getProperty("user.home");
    	String dubboResolvePath = userDir.concat("\\dubbo-resolve.properties");
    	Properties dubboResolve = new Properties();
    	try {
			InputStream inputStream=new FileInputStream(new File(dubboResolvePath));
			dubboResolve.load(inputStream);
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println(dubboResolve);
		return dubboResolve;
    }
    public static void main(String[] args) {
    	String userDir = System.getProperty("user.home");
    	String dubboResolvePath = userDir.concat("\\dubbo-resolve.properties");
    	Properties dubboResolve = new Properties();
    	try {
			InputStream inputStream=new FileInputStream(new File(dubboResolvePath));
			dubboResolve.load(inputStream);
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println(dubboResolve);
	}
}
